This notebook is the second part of an analysis that demonstrate samples in the PBTA cluster by cancer histology using dimensionality reduction techniques, Principal Component Analysis (PCA), t-Distributed Stochastic Neighbor Embedding (t-SNE), and Uniform Manifold Approximation and Projection (UMAP).

This notebook focuses on plotting the dimension reduction score data.

It addresses issue #9 in the Open-PBTA analysis repository.

Summary of Findings:

Upon plotting the dimension reduction scores produced by each of the three unique techniques mentioned above, it was noticed that there may be batch effects present.

  • The RNA_library variable within the metadata may be useful in correcting said batch effects as suggested by the selection strategy plots for both sets of expression data. That being said, we would want to repeat analyses with the data separated by the selection strategy method applied.
  • When plotting the data points colored by disease_type_new, it seemed that they are too many distinct values (cancer types) in this variable to be informative in the plots below. The variable broad_histology was used to replace disease_type_new for this reason.

Output Files:

  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_kallisto_histology.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_kallisto_strategy.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_polyA_kallisto_histology.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_rsem_histology.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_rsem_strategy.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_polyA_rsem_histology.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_stranded_kallisto_histology.pdf
  • analyses/transcriptomic-dimension-reduction/plots/meta_grid_stranded_rsem_histology.pdf # Usage

This script is intended to be run via the command line from the top directory of the repository as follows:

Rscript -e "rmarkdown::render('analyses/transcriptomic-dimension-reduction/02-transcriptomic-analysis-plotting.Rmd', clean = TRUE)"

Set Up

Assign functions.

# Function to plot
plot_dimension_reduction <-
  function(aligned_scores_df, point_color, score1, score2) {
    # Given a data.frame that contains the scores of a dimension reduction
    # analysis and the information we want from the metadata, make a scatterplot.
    #
    # Args:
    #   aligned_scores_df: data.frame containing dimension reduction scores,
    #                           and relative information from the metadata
    #   point_color: the variable whose information will be used to color
    #                the points on the plot
    #   score1: the column number of the first dimension reduction score that we
    #           want to plot
    #   score2: the column number of the second dimension reduction score that
    #           we want to plot
    #
    # Returns:
    #   dimension_reduction_plot: the plot representing the dimension reduction
    #                             scores in the given data.frame, using the values
    #                             in `point_color` as symbols to color the points

    # transform the strings in `point_color` into symbols for plotting
    color_sym <- rlang::sym(point_color)

    dimension_reduction_plot <- ggplot2::ggplot(
      aligned_scores_df,
      ggplot2::aes(
        x = dplyr::pull(aligned_scores_df, score1),
        y = dplyr::pull(aligned_scores_df, score2),
        color = !!color_sym
      )
    ) +
      ggplot2::geom_point(alpha = 0.3) +
      ggplot2::scale_color_manual(values = (c(
        "#2b3fff", "#ec102f", "#235e31",
        "#1a6587", "#11e38c", "#a22f80",
        "#fe5900", "#1945c5", "#51f310",
        "#8b20d3", "#799d10", "#881c23",
        "#3fc6f8", "#fe5cde", "#0a7fb2",
        "#f2945a", "#6b4472", "#f4d403",
        "#76480d", "#a6b6f9"
      )))

    return(dimension_reduction_plot)
  }

Assign name of the output directories.

# Assign names of output directories
results_dir <- "results"
plots_dir <- "plots"

# Create directory to hold the output.
if (!dir.exists(plots_dir)) {
  dir.create(plots_dir)
}

Read in the tsv files containing the dimension reduction scores aligned with the metadata, which were produced in 01-transcriptomic-analysis-prep.R.

### RSEM -----------------------------------------------------------------------
polyA_rsem_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_rsem_pca_scores_aligned.tsv"))

stranded_rsem_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_rsem_pca_scores_aligned.tsv"))

rsem_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "rsem_pca_scores_aligned.tsv"))

polyA_rsem_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_rsem_tsne_scores_aligned.tsv"))

stranded_rsem_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_rsem_tsne_scores_aligned.tsv"))

rsem_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "rsem_tsne_scores_aligned.tsv"))

polyA_rsem_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_rsem_umap_scores_aligned.tsv"))

stranded_rsem_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_rsem_umap_scores_aligned.tsv"))

rsem_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "rsem_umap_scores_aligned.tsv"))

### Kallisto -------------------------------------------------------------------
polyA_kallisto_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_kallisto_pca_scores_aligned.tsv"))

stranded_kallisto_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_kallisto_pca_scores_aligned.tsv"))

kallisto_pca_aligned_df <-
  readr::read_tsv(file.path(results_dir, "kallisto_pca_scores_aligned.tsv"))

polyA_kallisto_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_kallisto_tsne_scores_aligned.tsv"))

stranded_kallisto_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_kallisto_tsne_scores_aligned.tsv"))

kallisto_tsne_aligned_df <-
  readr::read_tsv(file.path(results_dir, "kallisto_tsne_scores_aligned.tsv"))

polyA_kallisto_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "polyA_kallisto_umap_scores_aligned.tsv"))

stranded_kallisto_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "stranded_kallisto_umap_scores_aligned.tsv"))

kallisto_umap_aligned_df <-
  readr::read_tsv(file.path(results_dir, "kallisto_umap_scores_aligned.tsv"))

Plot RSEM

Combined RSEM Plots Grid

The grid of plots below shows the dimension reductions scores for the RSEM expression data, colored by broad histology and selection strategy. These plots seem to suggest the presence of batch effects, which is likely due to the need for normalization across the two batches of RNA-Seq preparation (poly-A selection versus stranded(ribosomal depletion)). This can be further determined using the RNA_library column of the metadata to attempt to correct batch effects as suggested by the selection strategy grid of plots.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
rsem_pca_plot_histology <-
  plot_dimension_reduction(rsem_pca_aligned_df, "broad_histology", 1, 2)
rsem_tsne_plot_histology <-
  plot_dimension_reduction(rsem_tsne_aligned_df, "broad_histology", 1, 2)
rsem_umap_plot_histology <-
  plot_dimension_reduction(rsem_umap_aligned_df, "broad_histology", 1, 2)


# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `RNA_library` to color points
rsem_pca_plot_strategy <-
  plot_dimension_reduction(rsem_pca_aligned_df, "RNA_library", 1, 2)
rsem_tsne_plot_strategy <-
  plot_dimension_reduction(rsem_tsne_aligned_df, "RNA_library", 1, 2)
rsem_umap_plot_strategy <-
  plot_dimension_reduction(rsem_umap_aligned_df, "RNA_library", 1, 2)

# Extract the legend from one of the broad histology plots
legend_a <- cowplot::get_legend(
  rsem_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.05, 0.40),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 15)
    )
)

# Extract the legend from one of the selection strategy plots
legend_b <- cowplot::get_legend(
  rsem_umap_plot_strategy +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 15)
    )
)

# Plot grid with RSEM data colored by broad histology
rsem_grid_histology <- cowplot::plot_grid(
  rsem_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC2") +
    ggplot2::ggtitle("Broad Histology (RSEM)"),
  rsem_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  rsem_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 7
)

# Plot grid with RSEM data colored by selection strategy
rsem_grid_strategy <- cowplot::plot_grid(
  rsem_pca_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC2") +
    ggplot2::ggtitle("Selection Strategy (RSEM)"),
  rsem_tsne_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  rsem_umap_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 5
)

# Add the appropriate legends to the cowplots
rsem_grid_histology <-
  cowplot::plot_grid(rsem_grid_histology, legend_a, rel_heights = c(2, .2))

rsem_grid_strategy <-
  cowplot::plot_grid(rsem_grid_strategy, legend_b, rel_heights = c(2, 2))

# Save broad histology plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_rsem_histology.pdf"),
  rsem_grid_histology,
  width = 12,
  height = 15
)

# Save selection strategy plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_rsem_strategy.pdf"),
  rsem_grid_strategy,
  width = 12,
  height = 15
)

# Display the plot grid across broad histologies
rsem_grid_histology

# Display the plot grid across selection strategy
rsem_grid_strategy

Poly-A RSEM Plots Grid

The grid of plots below represents the RSEM data, filtered for the poly-A selection strategy, with data points colored by broad histology.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
polyA_rsem_pca_plot_histology <-
  plot_dimension_reduction(polyA_rsem_pca_aligned_df, "broad_histology", 1, 7)
polyA_rsem_tsne_plot_histology <-
  plot_dimension_reduction(polyA_rsem_tsne_aligned_df, "broad_histology", 1, 2)
polyA_rsem_umap_plot_histology <-
  plot_dimension_reduction(polyA_rsem_umap_aligned_df, "broad_histology", 1, 2)

# Plot grid with RSEM data colored by broad histology
polyA_rsem_grid_histology <- cowplot::plot_grid(
  polyA_rsem_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC7") +
    ggplot2::ggtitle("Broad Histology (RSEM - poly-A)"),
  polyA_rsem_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  polyA_rsem_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 6
)

# Extract the legend from one of the plots
legend_a <- cowplot::get_legend(
  polyA_rsem_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.001, 0.45),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 15)
    )
)

# Add the appropriate legend to the cowplot
polyA_rsem_grid_histology <-
  cowplot::plot_grid(polyA_rsem_grid_histology, legend_a, rel_heights = c(2, .2))

# Save plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_polyA_rsem_histology.pdf"),
  polyA_rsem_grid_histology,
  width = 12,
  height = 15
)
# Display the plot grid across broad histologies
polyA_rsem_grid_histology

Stranded RSEM Plots Grid

The grid of plots below represents the RSEM data, filtered for the stranded selection strategy, with data points colored by broad histology.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
stranded_rsem_pca_plot_histology <-
  plot_dimension_reduction(stranded_rsem_pca_aligned_df, "broad_histology", 2, 10)
stranded_rsem_tsne_plot_histology <-
  plot_dimension_reduction(stranded_rsem_tsne_aligned_df, "broad_histology", 1, 2)
stranded_rsem_umap_plot_histology <-
  plot_dimension_reduction(stranded_rsem_umap_aligned_df, "broad_histology", 1, 2)

# Extract the legend from one of the plots
legend_b <- cowplot::get_legend(
  stranded_rsem_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.05, 0.35),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 9)
    )
)

# Plot grid with RSEM data colored by broad histology
stranded_rsem_grid_histology <- cowplot::plot_grid(
  stranded_rsem_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC2", y = "PC10") +
    ggplot2::ggtitle("Broad Histology (RSEM - Stranded)"),
  stranded_rsem_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  stranded_rsem_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 6
)

# Add the appropriate legend to the cowplot
stranded_rsem_grid_histology <-
  cowplot::plot_grid(stranded_rsem_grid_histology, legend_b, rel_heights = c(2, .2))

# Save plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_stranded_rsem_histology.pdf"),
  stranded_rsem_grid_histology,
  width = 12,
  height = 15
)
# Display the plot grid across broad histologies
stranded_rsem_grid_histology

Plot Kallisto

Combined Kallisto Plots Grid

The grid of plots below shows the dimension reductions scores for the Kallisto expression data, colored by the broad histology and selection strategy. These plots seem to suggest the presence of batch effects, which is likely due to the need for normalization across the two batches of RNA-Seq preparation (poly-A selection versus stranded(ribosomal depletion)). This can be further determined using the RNA_library column of the metadata to attempt to correct batch effects as suggested by the selection strategy grid of plots.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
kallisto_pca_plot_histology <-
  plot_dimension_reduction(kallisto_pca_aligned_df, "broad_histology", 1, 2)
kallisto_tsne_plot_histology <-
  plot_dimension_reduction(kallisto_tsne_aligned_df, "broad_histology", 1, 2)
kallisto_umap_plot_histology <-
  plot_dimension_reduction(kallisto_umap_aligned_df, "broad_histology", 1, 2)

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `RNA_library` to color points
kallisto_pca_plot_strategy <-
  plot_dimension_reduction(kallisto_pca_aligned_df, "RNA_library", 1, 2)
kallisto_tsne_plot_strategy <-
  plot_dimension_reduction(kallisto_tsne_aligned_df, "RNA_library", 1, 2)
kallisto_umap_plot_strategy <-
  plot_dimension_reduction(kallisto_umap_aligned_df, "RNA_library", 1, 2)

# Extract the legend from one of the broad histology plots
legend_a <- cowplot::get_legend(
  kallisto_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.05, 0.40),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 9)
    )
)

# Extract the legend from one of the selection strategy plots
legend_b <- cowplot::get_legend(
  kallisto_umap_plot_strategy +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 15)
    )
)

# Plot grid with Kallisto data colored by broad histology
kallisto_grid_histology <- cowplot::plot_grid(
  kallisto_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC2") +
    ggplot2::ggtitle("Broad Histology (Kallisto)"),
  kallisto_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  kallisto_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 7
)

# Plot grid with Kallisto data colored by selection strategy
kallisto_grid_strategy <- cowplot::plot_grid(
  kallisto_pca_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC2") +
    ggplot2::ggtitle("Selection Strategy (Kallisto)"),
  kallisto_tsne_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  kallisto_umap_plot_strategy + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 5
)

# Add the appropriate legends to the cowplots
kallisto_grid_histology <-
  cowplot::plot_grid(kallisto_grid_histology, legend_a, rel_heights = c(2, .2))

kallisto_grid_strategy <-
  cowplot::plot_grid(kallisto_grid_strategy, legend_b, rel_heights = c(2, 2))

# Save broad histology plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_kallisto_histology.pdf"),
  kallisto_grid_histology,
  width = 12,
  height = 15
)

# Save selection strategy plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_kallisto_strategy.pdf"),
  kallisto_grid_strategy,
  width = 12,
  height = 15
)

# Display the plot grid across broad histologies
kallisto_grid_histology

# Display the plot grid across selection strategy
kallisto_grid_strategy

Poly-A Kallisto Plots Grid

The grid of plots below represents the Kallisto data filtered for the poly-A selection strategy, with data points colored by broad histology.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
polyA_kallisto_pca_plot_histology <-
  plot_dimension_reduction(polyA_kallisto_pca_aligned_df, "broad_histology", 1, 7)
polyA_kallisto_tsne_plot_histology <-
  plot_dimension_reduction(polyA_kallisto_tsne_aligned_df, "broad_histology", 1, 2)
polyA_kallisto_umap_plot_histology <-
  plot_dimension_reduction(polyA_kallisto_umap_aligned_df, "broad_histology", 1, 2)

# Plot grid with Kallisto data colored by cancer histology
polyA_kallisto_grid_histology <- cowplot::plot_grid(
  polyA_kallisto_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC1", y = "PC7") +
    ggplot2::ggtitle("Broad Histology (Kallisto - Poly-A)"),
  polyA_kallisto_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  polyA_kallisto_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 7
)

# Extract the legend from one of the plots
legend_a <- cowplot::get_legend(
  polyA_kallisto_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.001, 0.45),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 15)
    )
)

# Add the appropriate legend to the cowplot
polyA_kallisto_grid_histology <-
  cowplot::plot_grid(polyA_kallisto_grid_histology, legend_a, rel_heights = c(2, .2))

# Save plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_polyA_kallisto_histology.pdf"),
  polyA_kallisto_grid_histology,
  width = 12,
  height = 15
)

# Display the plot grid across broad histologies
polyA_kallisto_grid_histology

Stranded Kallisto Plots Grid

The grid of plots below represents the Kallisto data filtered for the stranded selection strategy, with data points colored by broad histology.

# Run the `plot_dimension_reduction` function to plot each set of dimension
# reduction scores using `broad_histology` to color points
stranded_kallisto_pca_plot_histology <-
  plot_dimension_reduction(stranded_kallisto_pca_aligned_df, "broad_histology", 4, 7)
stranded_kallisto_tsne_plot_histology <-
  plot_dimension_reduction(stranded_kallisto_tsne_aligned_df, "broad_histology", 1, 2)
stranded_kallisto_umap_plot_histology <-
  plot_dimension_reduction(stranded_kallisto_umap_aligned_df, "broad_histology", 1, 2)

# Extract the legend from one of the plots
legend_b <- cowplot::get_legend(
  stranded_kallisto_umap_plot_histology +
    ggplot2::theme(
      legend.direction = "vertical",
      legend.position = c(0.05, 0.35),
      legend.key.width = ggplot2::unit(0.1, "cm"),
      text = ggplot2::element_text(size = 9)
    )
)

# Plot grid with Kallisto data colored by broad histology
stranded_kallisto_grid_histology <- cowplot::plot_grid(
  stranded_kallisto_pca_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "PC4", y = "PC7") +
    ggplot2::ggtitle("Broad Histology (Kallisto - Stranded)"),
  stranded_kallisto_tsne_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "t-SNE1", y = "t-SNE2"),
  stranded_kallisto_umap_plot_histology + ggplot2::theme(legend.position = "none") +
    ggplot2::labs(x = "UMAP1", y = "UMAP2"),
  labels = "AUTO",
  align = "vh",
  axis = "b",
  ncol = 1,
  nrow = 7
)

# Add the appropriate legend to the cowplot
stranded_kallisto_grid_histology <-
  cowplot::plot_grid(stranded_kallisto_grid_histology, legend_b, rel_heights = c(2, .2))

# Save plot grid
ggplot2::ggsave(
  file.path("plots", "meta_grid_stranded_kallisto_histology.pdf"),
  stranded_kallisto_grid_histology,
  width = 12,
  height = 15
)

# Display the plot grid across broad histologies
stranded_kallisto_grid_histology

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       knitr_1.23       magrittr_1.5     hms_0.4.2       
 [5] cowplot_0.9.4    tidyselect_0.2.5 munsell_0.5.0    colorspace_1.4-1
 [9] R6_2.4.0         rlang_0.4.0      dplyr_0.8.3      stringr_1.4.0   
[13] tools_3.6.0      grid_3.6.0       gtable_0.3.0     xfun_0.8        
[17] htmltools_0.3.6  assertthat_0.2.1 lazyeval_0.2.2   yaml_2.2.0      
[21] digest_0.6.20    tibble_2.1.3     crayon_1.3.4     purrr_0.3.2     
[25] readr_1.3.1      ggplot2_3.2.0    base64enc_0.1-3  glue_1.3.1      
[29] evaluate_0.14    rmarkdown_1.13   labeling_0.3     stringi_1.4.3   
[33] compiler_3.6.0   pillar_1.4.2     scales_1.0.0     jsonlite_1.6    
[37] pkgconfig_2.0.2 
LS0tCnRpdGxlOiAiVW5zdXBlcnZpc2VkIEFuYWx5c2lzIG9mIFRyYW5zY3JpcHRvbWljIERpZmZlcmVuY2VzIC0gUGxvdHRpbmciCmF1dGhvcjogQ2hhbnRlIEJldGhlbGwgZm9yIENDREwgMjAxOQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKVGhpcyBub3RlYm9vayBpcyB0aGUgc2Vjb25kIHBhcnQgb2YgYW4gYW5hbHlzaXMgdGhhdCBkZW1vbnN0cmF0ZSBzYW1wbGVzIGluIHRoZSAKUEJUQSBjbHVzdGVyIGJ5IGNhbmNlciBoaXN0b2xvZ3kgdXNpbmcgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIHRlY2huaXF1ZXMsCltQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjAvdG9waWNzL3ByY29tcCkgKFBDQSksIApbdC1EaXN0cmlidXRlZCBTdG9jaGFzdGljIE5laWdoYm9yIEVtYmVkZGluZ10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1J0c25lL1J0c25lLnBkZikgKHQtU05FKSwgCmFuZCBbVW5pZm9ybSBNYW5pZm9sZCBBcHByb3hpbWF0aW9uIGFuZCBQcm9qZWN0aW9uXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdW1hcC92aWduZXR0ZXMvdW1hcC5odG1sKSAoVU1BUCkuCgpUaGlzIG5vdGVib29rIGZvY3VzZXMgb24gcGxvdHRpbmcgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb24gc2NvcmUgZGF0YS4gCgpJdCBhZGRyZXNzZXMgW2lzc3VlICM5XShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy9pc3N1ZXMvOSkKaW4gdGhlIE9wZW4tUEJUQSBhbmFseXNpcyByZXBvc2l0b3J5LiAKCiMjIFN1bW1hcnkgb2YgRmluZGluZ3M6CgpVcG9uIHBsb3R0aW5nIHRoZSBkaW1lbnNpb24gcmVkdWN0aW9uIHNjb3JlcyBwcm9kdWNlZCBieSBlYWNoIG9mIHRoZSB0aHJlZSAKdW5pcXVlIHRlY2huaXF1ZXMgbWVudGlvbmVkIGFib3ZlLCBpdCB3YXMgbm90aWNlZCB0aGF0IHRoZXJlIG1heSBiZSBiYXRjaCAKZWZmZWN0cyBwcmVzZW50LgoKLSBUaGUgYFJOQV9saWJyYXJ5YCB2YXJpYWJsZSB3aXRoaW4gdGhlIG1ldGFkYXRhIG1heSBiZSB1c2VmdWwgaW4gCiAgY29ycmVjdGluZyBzYWlkIGJhdGNoIGVmZmVjdHMgYXMgc3VnZ2VzdGVkIGJ5IHRoZSBzZWxlY3Rpb24gc3RyYXRlZ3kgcGxvdHMgCiAgZm9yIGJvdGggc2V0cyBvZiBleHByZXNzaW9uIGRhdGEuIFRoYXQgYmVpbmcgc2FpZCwgd2Ugd291bGQgd2FudCB0byByZXBlYXQgCiAgYW5hbHlzZXMgd2l0aCB0aGUgZGF0YSBzZXBhcmF0ZWQgYnkgdGhlIHNlbGVjdGlvbiBzdHJhdGVneSBtZXRob2QgYXBwbGllZC4gCi0gV2hlbiBwbG90dGluZyB0aGUgZGF0YSBwb2ludHMgY29sb3JlZCBieSBgZGlzZWFzZV90eXBlX25ld2AsIGl0IHNlZW1lZCB0aGF0CiAgdGhleSBhcmUgdG9vIG1hbnkgZGlzdGluY3QgdmFsdWVzIChjYW5jZXIgdHlwZXMpIGluIHRoaXMgdmFyaWFibGUgdG8gYmUKICBpbmZvcm1hdGl2ZSBpbiB0aGUgcGxvdHMgYmVsb3cuIFRoZSB2YXJpYWJsZSBgYnJvYWRfaGlzdG9sb2d5YCB3YXMgdXNlZCB0byAKICByZXBsYWNlIGBkaXNlYXNlX3R5cGVfbmV3YCBmb3IgdGhpcyByZWFzb24uIAoKIyMgT3V0cHV0IEZpbGVzOgotIGBhbmFseXNlcy90cmFuc2NyaXB0b21pYy1kaW1lbnNpb24tcmVkdWN0aW9uL3Bsb3RzL21ldGFfZ3JpZF9rYWxsaXN0b19oaXN0b2xvZ3kucGRmYAotIGBhbmFseXNlcy90cmFuc2NyaXB0b21pYy1kaW1lbnNpb24tcmVkdWN0aW9uL3Bsb3RzL21ldGFfZ3JpZF9rYWxsaXN0b19zdHJhdGVneS5wZGZgCi0gYGFuYWx5c2VzL3RyYW5zY3JpcHRvbWljLWRpbWVuc2lvbi1yZWR1Y3Rpb24vcGxvdHMvbWV0YV9ncmlkX3BvbHlBX2thbGxpc3RvX2hpc3RvbG9neS5wZGZgCi0gYGFuYWx5c2VzL3RyYW5zY3JpcHRvbWljLWRpbWVuc2lvbi1yZWR1Y3Rpb24vcGxvdHMvbWV0YV9ncmlkX3JzZW1faGlzdG9sb2d5LnBkZmAKLSBgYW5hbHlzZXMvdHJhbnNjcmlwdG9taWMtZGltZW5zaW9uLXJlZHVjdGlvbi9wbG90cy9tZXRhX2dyaWRfcnNlbV9zdHJhdGVneS5wZGZgCi0gYGFuYWx5c2VzL3RyYW5zY3JpcHRvbWljLWRpbWVuc2lvbi1yZWR1Y3Rpb24vcGxvdHMvbWV0YV9ncmlkX3BvbHlBX3JzZW1faGlzdG9sb2d5LnBkZmAKLSBgYW5hbHlzZXMvdHJhbnNjcmlwdG9taWMtZGltZW5zaW9uLXJlZHVjdGlvbi9wbG90cy9tZXRhX2dyaWRfc3RyYW5kZWRfa2FsbGlzdG9faGlzdG9sb2d5LnBkZmAKLSBgYW5hbHlzZXMvdHJhbnNjcmlwdG9taWMtZGltZW5zaW9uLXJlZHVjdGlvbi9wbG90cy9tZXRhX2dyaWRfc3RyYW5kZWRfcnNlbV9oaXN0b2xvZ3kucGRmYAojIFVzYWdlCgpUaGlzIHNjcmlwdCBpcyBpbnRlbmRlZCB0byBiZSBydW4gdmlhIHRoZSBjb21tYW5kIGxpbmUgZnJvbSB0aGUgdG9wIGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSBhcyBmb2xsb3dzOiAKYGBgClJzY3JpcHQgLWUgInJtYXJrZG93bjo6cmVuZGVyKCdhbmFseXNlcy90cmFuc2NyaXB0b21pYy1kaW1lbnNpb24tcmVkdWN0aW9uLzAyLXRyYW5zY3JpcHRvbWljLWFuYWx5c2lzLXBsb3R0aW5nLlJtZCcsIGNsZWFuID0gVFJVRSkiCmBgYAoKIyBTZXQgVXAKCkFzc2lnbiBmdW5jdGlvbnMuIApgYGB7cn0KIyBGdW5jdGlvbiB0byBwbG90CnBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbiA8LQogIGZ1bmN0aW9uKGFsaWduZWRfc2NvcmVzX2RmLCBwb2ludF9jb2xvciwgc2NvcmUxLCBzY29yZTIpIHsKICAgICMgR2l2ZW4gYSBkYXRhLmZyYW1lIHRoYXQgY29udGFpbnMgdGhlIHNjb3JlcyBvZiBhIGRpbWVuc2lvbiByZWR1Y3Rpb24KICAgICMgYW5hbHlzaXMgYW5kIHRoZSBpbmZvcm1hdGlvbiB3ZSB3YW50IGZyb20gdGhlIG1ldGFkYXRhLCBtYWtlIGEgc2NhdHRlcnBsb3QuCiAgICAjCiAgICAjIEFyZ3M6CiAgICAjICAgYWxpZ25lZF9zY29yZXNfZGY6IGRhdGEuZnJhbWUgY29udGFpbmluZyBkaW1lbnNpb24gcmVkdWN0aW9uIHNjb3JlcywKICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgcmVsYXRpdmUgaW5mb3JtYXRpb24gZnJvbSB0aGUgbWV0YWRhdGEKICAgICMgICBwb2ludF9jb2xvcjogdGhlIHZhcmlhYmxlIHdob3NlIGluZm9ybWF0aW9uIHdpbGwgYmUgdXNlZCB0byBjb2xvcgogICAgIyAgICAgICAgICAgICAgICB0aGUgcG9pbnRzIG9uIHRoZSBwbG90CiAgICAjICAgc2NvcmUxOiB0aGUgY29sdW1uIG51bWJlciBvZiB0aGUgZmlyc3QgZGltZW5zaW9uIHJlZHVjdGlvbiBzY29yZSB0aGF0IHdlCiAgICAjICAgICAgICAgICB3YW50IHRvIHBsb3QKICAgICMgICBzY29yZTI6IHRoZSBjb2x1bW4gbnVtYmVyIG9mIHRoZSBzZWNvbmQgZGltZW5zaW9uIHJlZHVjdGlvbiBzY29yZSB0aGF0CiAgICAjICAgICAgICAgICB3ZSB3YW50IHRvIHBsb3QKICAgICMKICAgICMgUmV0dXJuczoKICAgICMgICBkaW1lbnNpb25fcmVkdWN0aW9uX3Bsb3Q6IHRoZSBwbG90IHJlcHJlc2VudGluZyB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbgogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NvcmVzIGluIHRoZSBnaXZlbiBkYXRhLmZyYW1lLCB1c2luZyB0aGUgdmFsdWVzCiAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbiBgcG9pbnRfY29sb3JgIGFzIHN5bWJvbHMgdG8gY29sb3IgdGhlIHBvaW50cwoKICAgICMgdHJhbnNmb3JtIHRoZSBzdHJpbmdzIGluIGBwb2ludF9jb2xvcmAgaW50byBzeW1ib2xzIGZvciBwbG90dGluZwogICAgY29sb3Jfc3ltIDwtIHJsYW5nOjpzeW0ocG9pbnRfY29sb3IpCgogICAgZGltZW5zaW9uX3JlZHVjdGlvbl9wbG90IDwtIGdncGxvdDI6OmdncGxvdCgKICAgICAgYWxpZ25lZF9zY29yZXNfZGYsCiAgICAgIGdncGxvdDI6OmFlcygKICAgICAgICB4ID0gZHBseXI6OnB1bGwoYWxpZ25lZF9zY29yZXNfZGYsIHNjb3JlMSksCiAgICAgICAgeSA9IGRwbHlyOjpwdWxsKGFsaWduZWRfc2NvcmVzX2RmLCBzY29yZTIpLAogICAgICAgIGNvbG9yID0gISFjb2xvcl9zeW0KICAgICAgKQogICAgKSArCiAgICAgIGdncGxvdDI6Omdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsKICAgICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IChjKAogICAgICAgICIjMmIzZmZmIiwgIiNlYzEwMmYiLCAiIzIzNWUzMSIsCiAgICAgICAgIiMxYTY1ODciLCAiIzExZTM4YyIsICIjYTIyZjgwIiwKICAgICAgICAiI2ZlNTkwMCIsICIjMTk0NWM1IiwgIiM1MWYzMTAiLAogICAgICAgICIjOGIyMGQzIiwgIiM3OTlkMTAiLCAiIzg4MWMyMyIsCiAgICAgICAgIiMzZmM2ZjgiLCAiI2ZlNWNkZSIsICIjMGE3ZmIyIiwKICAgICAgICAiI2YyOTQ1YSIsICIjNmI0NDcyIiwgIiNmNGQ0MDMiLAogICAgICAgICIjNzY0ODBkIiwgIiNhNmI2ZjkiCiAgICAgICkpKQoKICAgIHJldHVybihkaW1lbnNpb25fcmVkdWN0aW9uX3Bsb3QpCiAgfQpgYGAKCkFzc2lnbiBuYW1lIG9mIHRoZSBvdXRwdXQgZGlyZWN0b3JpZXMuCmBgYHtyfQojIEFzc2lnbiBuYW1lcyBvZiBvdXRwdXQgZGlyZWN0b3JpZXMKcmVzdWx0c19kaXIgPC0gInJlc3VsdHMiCnBsb3RzX2RpciA8LSAicGxvdHMiCgojIENyZWF0ZSBkaXJlY3RvcnkgdG8gaG9sZCB0aGUgb3V0cHV0LgppZiAoIWRpci5leGlzdHMocGxvdHNfZGlyKSkgewogIGRpci5jcmVhdGUocGxvdHNfZGlyKQp9CmBgYAoKUmVhZCBpbiB0aGUgdHN2IGZpbGVzIGNvbnRhaW5pbmcgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb24gc2NvcmVzIGFsaWduZWQgd2l0aCAKdGhlIG1ldGFkYXRhLCB3aGljaCB3ZXJlIHByb2R1Y2VkIGluIGAwMS10cmFuc2NyaXB0b21pYy1hbmFseXNpcy1wcmVwLlJgLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojIyMgUlNFTSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwb2x5QV9yc2VtX3BjYV9hbGlnbmVkX2RmIDwtCiAgcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgInBvbHlBX3JzZW1fcGNhX3Njb3Jlc19hbGlnbmVkLnRzdiIpKQoKc3RyYW5kZWRfcnNlbV9wY2FfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9yc2VtX3BjYV9zY29yZXNfYWxpZ25lZC50c3YiKSkKCnJzZW1fcGNhX2FsaWduZWRfZGYgPC0KICByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAicnNlbV9wY2Ffc2NvcmVzX2FsaWduZWQudHN2IikpCgpwb2x5QV9yc2VtX3RzbmVfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJwb2x5QV9yc2VtX3RzbmVfc2NvcmVzX2FsaWduZWQudHN2IikpCgpzdHJhbmRlZF9yc2VtX3RzbmVfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9yc2VtX3RzbmVfc2NvcmVzX2FsaWduZWQudHN2IikpCgpyc2VtX3RzbmVfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJyc2VtX3RzbmVfc2NvcmVzX2FsaWduZWQudHN2IikpCgpwb2x5QV9yc2VtX3VtYXBfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJwb2x5QV9yc2VtX3VtYXBfc2NvcmVzX2FsaWduZWQudHN2IikpCgpzdHJhbmRlZF9yc2VtX3VtYXBfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9yc2VtX3VtYXBfc2NvcmVzX2FsaWduZWQudHN2IikpCgpyc2VtX3VtYXBfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJyc2VtX3VtYXBfc2NvcmVzX2FsaWduZWQudHN2IikpCgojIyMgS2FsbGlzdG8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpwb2x5QV9rYWxsaXN0b19wY2FfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJwb2x5QV9rYWxsaXN0b19wY2Ffc2NvcmVzX2FsaWduZWQudHN2IikpCgpzdHJhbmRlZF9rYWxsaXN0b19wY2FfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9rYWxsaXN0b19wY2Ffc2NvcmVzX2FsaWduZWQudHN2IikpCgprYWxsaXN0b19wY2FfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJrYWxsaXN0b19wY2Ffc2NvcmVzX2FsaWduZWQudHN2IikpCgpwb2x5QV9rYWxsaXN0b190c25lX2FsaWduZWRfZGYgPC0KICByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAicG9seUFfa2FsbGlzdG9fdHNuZV9zY29yZXNfYWxpZ25lZC50c3YiKSkKCnN0cmFuZGVkX2thbGxpc3RvX3RzbmVfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9rYWxsaXN0b190c25lX3Njb3Jlc19hbGlnbmVkLnRzdiIpKQoKa2FsbGlzdG9fdHNuZV9hbGlnbmVkX2RmIDwtCiAgcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImthbGxpc3RvX3RzbmVfc2NvcmVzX2FsaWduZWQudHN2IikpCgpwb2x5QV9rYWxsaXN0b191bWFwX2FsaWduZWRfZGYgPC0KICByZWFkcjo6cmVhZF90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAicG9seUFfa2FsbGlzdG9fdW1hcF9zY29yZXNfYWxpZ25lZC50c3YiKSkKCnN0cmFuZGVkX2thbGxpc3RvX3VtYXBfYWxpZ25lZF9kZiA8LQogIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzdHJhbmRlZF9rYWxsaXN0b191bWFwX3Njb3Jlc19hbGlnbmVkLnRzdiIpKQoKa2FsbGlzdG9fdW1hcF9hbGlnbmVkX2RmIDwtCiAgcmVhZHI6OnJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImthbGxpc3RvX3VtYXBfc2NvcmVzX2FsaWduZWQudHN2IikpCmBgYAoKCiMgUGxvdCBSU0VNCgojIyBDb21iaW5lZCBSU0VNIFBsb3RzIEdyaWQKVGhlIGdyaWQgb2YgcGxvdHMgYmVsb3cgc2hvd3MgdGhlIGRpbWVuc2lvbiByZWR1Y3Rpb25zIHNjb3JlcyBmb3IgdGhlIFJTRU0gCmV4cHJlc3Npb24gZGF0YSwgY29sb3JlZCBieSBicm9hZCBoaXN0b2xvZ3kgYW5kIHNlbGVjdGlvbiBzdHJhdGVneS4KVGhlc2UgcGxvdHMgc2VlbSB0byBzdWdnZXN0IHRoZSBwcmVzZW5jZSBvZiBiYXRjaCBlZmZlY3RzLCB3aGljaCBpcyBsaWtlbHkgZHVlIAp0byB0aGUgbmVlZCBmb3Igbm9ybWFsaXphdGlvbiBhY3Jvc3MgdGhlIHR3byBiYXRjaGVzIG9mIFJOQS1TZXEgcHJlcGFyYXRpb24gCihwb2x5LUEgc2VsZWN0aW9uIHZlcnN1cyBzdHJhbmRlZChyaWJvc29tYWwgZGVwbGV0aW9uKSkuClRoaXMgY2FuIGJlIGZ1cnRoZXIgZGV0ZXJtaW5lZCB1c2luZyB0aGUgYFJOQV9saWJyYXJ5YCBjb2x1bW4gb2YgdGhlCm1ldGFkYXRhIHRvIGF0dGVtcHQgdG8gY29ycmVjdCBiYXRjaCBlZmZlY3RzIGFzIHN1Z2dlc3RlZCBieSB0aGUgc2VsZWN0aW9uCnN0cmF0ZWd5IGdyaWQgb2YgcGxvdHMuCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxOH0KIyBSdW4gdGhlIGBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb25gIGZ1bmN0aW9uIHRvIHBsb3QgZWFjaCBzZXQgb2YgZGltZW5zaW9uCiMgcmVkdWN0aW9uIHNjb3JlcyB1c2luZyBgYnJvYWRfaGlzdG9sb2d5YCB0byBjb2xvciBwb2ludHMKcnNlbV9wY2FfcGxvdF9oaXN0b2xvZ3kgPC0KICBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb24ocnNlbV9wY2FfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDIpCnJzZW1fdHNuZV9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihyc2VtX3RzbmVfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDIpCnJzZW1fdW1hcF9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihyc2VtX3VtYXBfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDIpCgoKIyBSdW4gdGhlIGBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb25gIGZ1bmN0aW9uIHRvIHBsb3QgZWFjaCBzZXQgb2YgZGltZW5zaW9uCiMgcmVkdWN0aW9uIHNjb3JlcyB1c2luZyBgUk5BX2xpYnJhcnlgIHRvIGNvbG9yIHBvaW50cwpyc2VtX3BjYV9wbG90X3N0cmF0ZWd5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHJzZW1fcGNhX2FsaWduZWRfZGYsICJSTkFfbGlicmFyeSIsIDEsIDIpCnJzZW1fdHNuZV9wbG90X3N0cmF0ZWd5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHJzZW1fdHNuZV9hbGlnbmVkX2RmLCAiUk5BX2xpYnJhcnkiLCAxLCAyKQpyc2VtX3VtYXBfcGxvdF9zdHJhdGVneSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihyc2VtX3VtYXBfYWxpZ25lZF9kZiwgIlJOQV9saWJyYXJ5IiwgMSwgMikKCiMgRXh0cmFjdCB0aGUgbGVnZW5kIGZyb20gb25lIG9mIHRoZSBicm9hZCBoaXN0b2xvZ3kgcGxvdHMKbGVnZW5kX2EgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZCgKICByc2VtX3VtYXBfcGxvdF9oaXN0b2xvZ3kgKwogICAgZ2dwbG90Mjo6dGhlbWUoCiAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLAogICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDUsIDAuNDApLAogICAgICBsZWdlbmQua2V5LndpZHRoID0gZ2dwbG90Mjo6dW5pdCgwLjEsICJjbSIpLAogICAgICB0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkKICAgICkKKQoKIyBFeHRyYWN0IHRoZSBsZWdlbmQgZnJvbSBvbmUgb2YgdGhlIHNlbGVjdGlvbiBzdHJhdGVneSBwbG90cwpsZWdlbmRfYiA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKAogIHJzZW1fdW1hcF9wbG90X3N0cmF0ZWd5ICsKICAgIGdncGxvdDI6OnRoZW1lKAogICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgbGVnZW5kLmtleS53aWR0aCA9IGdncGxvdDI6OnVuaXQoMC4xLCAiY20iKSwKICAgICAgdGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTUpCiAgICApCikKCiMgUGxvdCBncmlkIHdpdGggUlNFTSBkYXRhIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5CnJzZW1fZ3JpZF9oaXN0b2xvZ3kgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIHJzZW1fcGNhX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzIiKSArCiAgICBnZ3Bsb3QyOjpnZ3RpdGxlKCJCcm9hZCBIaXN0b2xvZ3kgKFJTRU0pIiksCiAgcnNlbV90c25lX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAidC1TTkUxIiwgeSA9ICJ0LVNORTIiKSwKICByc2VtX3VtYXBfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJVTUFQMSIsIHkgPSAiVU1BUDIiKSwKICBsYWJlbHMgPSAiQVVUTyIsCiAgYWxpZ24gPSAidmgiLAogIGF4aXMgPSAiYiIsCiAgbmNvbCA9IDEsCiAgbnJvdyA9IDcKKQoKIyBQbG90IGdyaWQgd2l0aCBSU0VNIGRhdGEgY29sb3JlZCBieSBzZWxlY3Rpb24gc3RyYXRlZ3kKcnNlbV9ncmlkX3N0cmF0ZWd5IDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICByc2VtX3BjYV9wbG90X3N0cmF0ZWd5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzIiKSArCiAgICBnZ3Bsb3QyOjpnZ3RpdGxlKCJTZWxlY3Rpb24gU3RyYXRlZ3kgKFJTRU0pIiksCiAgcnNlbV90c25lX3Bsb3Rfc3RyYXRlZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJ0LVNORTEiLCB5ID0gInQtU05FMiIpLAogIHJzZW1fdW1hcF9wbG90X3N0cmF0ZWd5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiVU1BUDEiLCB5ID0gIlVNQVAyIiksCiAgbGFiZWxzID0gIkFVVE8iLAogIGFsaWduID0gInZoIiwKICBheGlzID0gImIiLAogIG5jb2wgPSAxLAogIG5yb3cgPSA1CikKCiMgQWRkIHRoZSBhcHByb3ByaWF0ZSBsZWdlbmRzIHRvIHRoZSBjb3dwbG90cwpyc2VtX2dyaWRfaGlzdG9sb2d5IDwtCiAgY293cGxvdDo6cGxvdF9ncmlkKHJzZW1fZ3JpZF9oaXN0b2xvZ3ksIGxlZ2VuZF9hLCByZWxfaGVpZ2h0cyA9IGMoMiwgLjIpKQoKcnNlbV9ncmlkX3N0cmF0ZWd5IDwtCiAgY293cGxvdDo6cGxvdF9ncmlkKHJzZW1fZ3JpZF9zdHJhdGVneSwgbGVnZW5kX2IsIHJlbF9oZWlnaHRzID0gYygyLCAyKSkKCiMgU2F2ZSBicm9hZCBoaXN0b2xvZ3kgcGxvdCBncmlkCmdncGxvdDI6Omdnc2F2ZSgKICBmaWxlLnBhdGgoInBsb3RzIiwgIm1ldGFfZ3JpZF9yc2VtX2hpc3RvbG9neS5wZGYiKSwKICByc2VtX2dyaWRfaGlzdG9sb2d5LAogIHdpZHRoID0gMTIsCiAgaGVpZ2h0ID0gMTUKKQoKIyBTYXZlIHNlbGVjdGlvbiBzdHJhdGVneSBwbG90IGdyaWQKZ2dwbG90Mjo6Z2dzYXZlKAogIGZpbGUucGF0aCgicGxvdHMiLCAibWV0YV9ncmlkX3JzZW1fc3RyYXRlZ3kucGRmIiksCiAgcnNlbV9ncmlkX3N0cmF0ZWd5LAogIHdpZHRoID0gMTIsCiAgaGVpZ2h0ID0gMTUKKQoKIyBEaXNwbGF5IHRoZSBwbG90IGdyaWQgYWNyb3NzIGJyb2FkIGhpc3RvbG9naWVzCnJzZW1fZ3JpZF9oaXN0b2xvZ3kKCiMgRGlzcGxheSB0aGUgcGxvdCBncmlkIGFjcm9zcyBzZWxlY3Rpb24gc3RyYXRlZ3kKcnNlbV9ncmlkX3N0cmF0ZWd5CmBgYAojIyBQb2x5LUEgUlNFTSBQbG90cyBHcmlkIApUaGUgZ3JpZCBvZiBwbG90cyBiZWxvdyByZXByZXNlbnRzIHRoZSBSU0VNIGRhdGEsIGZpbHRlcmVkIGZvciB0aGUgYHBvbHktQWAKc2VsZWN0aW9uIHN0cmF0ZWd5LCB3aXRoIGRhdGEgcG9pbnRzIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5LiAKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDE4fQojIFJ1biB0aGUgYHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbmAgZnVuY3Rpb24gdG8gcGxvdCBlYWNoIHNldCBvZiBkaW1lbnNpb24KIyByZWR1Y3Rpb24gc2NvcmVzIHVzaW5nIGBicm9hZF9oaXN0b2xvZ3lgIHRvIGNvbG9yIHBvaW50cwpwb2x5QV9yc2VtX3BjYV9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihwb2x5QV9yc2VtX3BjYV9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgNykKcG9seUFfcnNlbV90c25lX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHBvbHlBX3JzZW1fdHNuZV9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKcG9seUFfcnNlbV91bWFwX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHBvbHlBX3JzZW1fdW1hcF9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKCiMgUGxvdCBncmlkIHdpdGggUlNFTSBkYXRhIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5CnBvbHlBX3JzZW1fZ3JpZF9oaXN0b2xvZ3kgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIHBvbHlBX3JzZW1fcGNhX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzciKSArCiAgICBnZ3Bsb3QyOjpnZ3RpdGxlKCJCcm9hZCBIaXN0b2xvZ3kgKFJTRU0gLSBwb2x5LUEpIiksCiAgcG9seUFfcnNlbV90c25lX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAidC1TTkUxIiwgeSA9ICJ0LVNORTIiKSwKICBwb2x5QV9yc2VtX3VtYXBfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJVTUFQMSIsIHkgPSAiVU1BUDIiKSwKICBsYWJlbHMgPSAiQVVUTyIsCiAgYWxpZ24gPSAidmgiLAogIGF4aXMgPSAiYiIsCiAgbmNvbCA9IDEsCiAgbnJvdyA9IDYKKQoKIyBFeHRyYWN0IHRoZSBsZWdlbmQgZnJvbSBvbmUgb2YgdGhlIHBsb3RzCmxlZ2VuZF9hIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQoCiAgcG9seUFfcnNlbV91bWFwX3Bsb3RfaGlzdG9sb2d5ICsKICAgIGdncGxvdDI6OnRoZW1lKAogICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjAwMSwgMC40NSksCiAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSBnZ3Bsb3QyOjp1bml0KDAuMSwgImNtIiksCiAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KQogICAgKQopCgojIEFkZCB0aGUgYXBwcm9wcmlhdGUgbGVnZW5kIHRvIHRoZSBjb3dwbG90CnBvbHlBX3JzZW1fZ3JpZF9oaXN0b2xvZ3kgPC0KICBjb3dwbG90OjpwbG90X2dyaWQocG9seUFfcnNlbV9ncmlkX2hpc3RvbG9neSwgbGVnZW5kX2EsIHJlbF9oZWlnaHRzID0gYygyLCAuMikpCgojIFNhdmUgcGxvdCBncmlkCmdncGxvdDI6Omdnc2F2ZSgKICBmaWxlLnBhdGgoInBsb3RzIiwgIm1ldGFfZ3JpZF9wb2x5QV9yc2VtX2hpc3RvbG9neS5wZGYiKSwKICBwb2x5QV9yc2VtX2dyaWRfaGlzdG9sb2d5LAogIHdpZHRoID0gMTIsCiAgaGVpZ2h0ID0gMTUKKQojIERpc3BsYXkgdGhlIHBsb3QgZ3JpZCBhY3Jvc3MgYnJvYWQgaGlzdG9sb2dpZXMKcG9seUFfcnNlbV9ncmlkX2hpc3RvbG9neQpgYGAKCiMjIFN0cmFuZGVkIFJTRU0gUGxvdHMgR3JpZApUaGUgZ3JpZCBvZiBwbG90cyBiZWxvdyByZXByZXNlbnRzIHRoZSBSU0VNIGRhdGEsIGZpbHRlcmVkIGZvciB0aGUgYHN0cmFuZGVkYApzZWxlY3Rpb24gc3RyYXRlZ3ksIHdpdGggZGF0YSBwb2ludHMgY29sb3JlZCBieSBicm9hZCBoaXN0b2xvZ3kuIAoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTh9CiMgUnVuIHRoZSBgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uYCBmdW5jdGlvbiB0byBwbG90IGVhY2ggc2V0IG9mIGRpbWVuc2lvbgojIHJlZHVjdGlvbiBzY29yZXMgdXNpbmcgYGJyb2FkX2hpc3RvbG9neWAgdG8gY29sb3IgcG9pbnRzCnN0cmFuZGVkX3JzZW1fcGNhX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHN0cmFuZGVkX3JzZW1fcGNhX2FsaWduZWRfZGYsICJicm9hZF9oaXN0b2xvZ3kiLCAyLCAxMCkKc3RyYW5kZWRfcnNlbV90c25lX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHN0cmFuZGVkX3JzZW1fdHNuZV9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKc3RyYW5kZWRfcnNlbV91bWFwX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHN0cmFuZGVkX3JzZW1fdW1hcF9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKCiMgRXh0cmFjdCB0aGUgbGVnZW5kIGZyb20gb25lIG9mIHRoZSBwbG90cwpsZWdlbmRfYiA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKAogIHN0cmFuZGVkX3JzZW1fdW1hcF9wbG90X2hpc3RvbG9neSArCiAgICBnZ3Bsb3QyOjp0aGVtZSgKICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wNSwgMC4zNSksCiAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSBnZ3Bsb3QyOjp1bml0KDAuMSwgImNtIiksCiAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDkpCiAgICApCikKCiMgUGxvdCBncmlkIHdpdGggUlNFTSBkYXRhIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5CnN0cmFuZGVkX3JzZW1fZ3JpZF9oaXN0b2xvZ3kgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIHN0cmFuZGVkX3JzZW1fcGNhX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiUEMyIiwgeSA9ICJQQzEwIikgKwogICAgZ2dwbG90Mjo6Z2d0aXRsZSgiQnJvYWQgSGlzdG9sb2d5IChSU0VNIC0gU3RyYW5kZWQpIiksCiAgc3RyYW5kZWRfcnNlbV90c25lX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAidC1TTkUxIiwgeSA9ICJ0LVNORTIiKSwKICBzdHJhbmRlZF9yc2VtX3VtYXBfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJVTUFQMSIsIHkgPSAiVU1BUDIiKSwKICBsYWJlbHMgPSAiQVVUTyIsCiAgYWxpZ24gPSAidmgiLAogIGF4aXMgPSAiYiIsCiAgbmNvbCA9IDEsCiAgbnJvdyA9IDYKKQoKIyBBZGQgdGhlIGFwcHJvcHJpYXRlIGxlZ2VuZCB0byB0aGUgY293cGxvdApzdHJhbmRlZF9yc2VtX2dyaWRfaGlzdG9sb2d5IDwtCiAgY293cGxvdDo6cGxvdF9ncmlkKHN0cmFuZGVkX3JzZW1fZ3JpZF9oaXN0b2xvZ3ksIGxlZ2VuZF9iLCByZWxfaGVpZ2h0cyA9IGMoMiwgLjIpKQoKIyBTYXZlIHBsb3QgZ3JpZApnZ3Bsb3QyOjpnZ3NhdmUoCiAgZmlsZS5wYXRoKCJwbG90cyIsICJtZXRhX2dyaWRfc3RyYW5kZWRfcnNlbV9oaXN0b2xvZ3kucGRmIiksCiAgc3RyYW5kZWRfcnNlbV9ncmlkX2hpc3RvbG9neSwKICB3aWR0aCA9IDEyLAogIGhlaWdodCA9IDE1CikKIyBEaXNwbGF5IHRoZSBwbG90IGdyaWQgYWNyb3NzIGJyb2FkIGhpc3RvbG9naWVzCnN0cmFuZGVkX3JzZW1fZ3JpZF9oaXN0b2xvZ3kKYGBgCiMgUGxvdCBLYWxsaXN0bwoKIyMgQ29tYmluZWQgS2FsbGlzdG8gUGxvdHMgR3JpZApUaGUgZ3JpZCBvZiBwbG90cyBiZWxvdyBzaG93cyB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbnMgc2NvcmVzIGZvciB0aGUgS2FsbGlzdG8gCmV4cHJlc3Npb24gZGF0YSwgY29sb3JlZCBieSB0aGUgYnJvYWQgaGlzdG9sb2d5IGFuZCBzZWxlY3Rpb24gc3RyYXRlZ3kuClRoZXNlIHBsb3RzIHNlZW0gdG8gc3VnZ2VzdCB0aGUgcHJlc2VuY2Ugb2YgYmF0Y2ggZWZmZWN0cywgd2hpY2ggaXMgbGlrZWx5IGR1ZSAKdG8gdGhlIG5lZWQgZm9yIG5vcm1hbGl6YXRpb24gYWNyb3NzIHRoZSB0d28gYmF0Y2hlcyBvZiBSTkEtU2VxIHByZXBhcmF0aW9uIAoocG9seS1BIHNlbGVjdGlvbiB2ZXJzdXMgc3RyYW5kZWQocmlib3NvbWFsIGRlcGxldGlvbikpLgpUaGlzIGNhbiBiZSBmdXJ0aGVyIGRldGVybWluZWQgdXNpbmcgdGhlIGBSTkFfbGlicmFyeWAgY29sdW1uIG9mIHRoZQptZXRhZGF0YSB0byBhdHRlbXB0IHRvIGNvcnJlY3QgYmF0Y2ggZWZmZWN0cyBhcyBzdWdnZXN0ZWQgYnkgdGhlIHNlbGVjdGlvbiAKc3RyYXRlZ3kgZ3JpZCBvZiBwbG90cy4KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDE4fQojIFJ1biB0aGUgYHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbmAgZnVuY3Rpb24gdG8gcGxvdCBlYWNoIHNldCBvZiBkaW1lbnNpb24KIyByZWR1Y3Rpb24gc2NvcmVzIHVzaW5nIGBicm9hZF9oaXN0b2xvZ3lgIHRvIGNvbG9yIHBvaW50cwprYWxsaXN0b19wY2FfcGxvdF9oaXN0b2xvZ3kgPC0KICBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb24oa2FsbGlzdG9fcGNhX2FsaWduZWRfZGYsICJicm9hZF9oaXN0b2xvZ3kiLCAxLCAyKQprYWxsaXN0b190c25lX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKGthbGxpc3RvX3RzbmVfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDIpCmthbGxpc3RvX3VtYXBfcGxvdF9oaXN0b2xvZ3kgPC0KICBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb24oa2FsbGlzdG9fdW1hcF9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKCiMgUnVuIHRoZSBgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uYCBmdW5jdGlvbiB0byBwbG90IGVhY2ggc2V0IG9mIGRpbWVuc2lvbgojIHJlZHVjdGlvbiBzY29yZXMgdXNpbmcgYFJOQV9saWJyYXJ5YCB0byBjb2xvciBwb2ludHMKa2FsbGlzdG9fcGNhX3Bsb3Rfc3RyYXRlZ3kgPC0KICBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb24oa2FsbGlzdG9fcGNhX2FsaWduZWRfZGYsICJSTkFfbGlicmFyeSIsIDEsIDIpCmthbGxpc3RvX3RzbmVfcGxvdF9zdHJhdGVneSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihrYWxsaXN0b190c25lX2FsaWduZWRfZGYsICJSTkFfbGlicmFyeSIsIDEsIDIpCmthbGxpc3RvX3VtYXBfcGxvdF9zdHJhdGVneSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihrYWxsaXN0b191bWFwX2FsaWduZWRfZGYsICJSTkFfbGlicmFyeSIsIDEsIDIpCgojIEV4dHJhY3QgdGhlIGxlZ2VuZCBmcm9tIG9uZSBvZiB0aGUgYnJvYWQgaGlzdG9sb2d5IHBsb3RzCmxlZ2VuZF9hIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQoCiAga2FsbGlzdG9fdW1hcF9wbG90X2hpc3RvbG9neSArCiAgICBnZ3Bsb3QyOjp0aGVtZSgKICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wNSwgMC40MCksCiAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSBnZ3Bsb3QyOjp1bml0KDAuMSwgImNtIiksCiAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDkpCiAgICApCikKCiMgRXh0cmFjdCB0aGUgbGVnZW5kIGZyb20gb25lIG9mIHRoZSBzZWxlY3Rpb24gc3RyYXRlZ3kgcGxvdHMKbGVnZW5kX2IgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZCgKICBrYWxsaXN0b191bWFwX3Bsb3Rfc3RyYXRlZ3kgKwogICAgZ2dwbG90Mjo6dGhlbWUoCiAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiLAogICAgICBsZWdlbmQua2V5LndpZHRoID0gZ2dwbG90Mjo6dW5pdCgwLjEsICJjbSIpLAogICAgICB0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkKICAgICkKKQoKIyBQbG90IGdyaWQgd2l0aCBLYWxsaXN0byBkYXRhIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5CmthbGxpc3RvX2dyaWRfaGlzdG9sb2d5IDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICBrYWxsaXN0b19wY2FfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMiIpICsKICAgIGdncGxvdDI6OmdndGl0bGUoIkJyb2FkIEhpc3RvbG9neSAoS2FsbGlzdG8pIiksCiAga2FsbGlzdG9fdHNuZV9wbG90X2hpc3RvbG9neSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2dwbG90Mjo6bGFicyh4ID0gInQtU05FMSIsIHkgPSAidC1TTkUyIiksCiAga2FsbGlzdG9fdW1hcF9wbG90X2hpc3RvbG9neSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2dwbG90Mjo6bGFicyh4ID0gIlVNQVAxIiwgeSA9ICJVTUFQMiIpLAogIGxhYmVscyA9ICJBVVRPIiwKICBhbGlnbiA9ICJ2aCIsCiAgYXhpcyA9ICJiIiwKICBuY29sID0gMSwKICBucm93ID0gNwopCgojIFBsb3QgZ3JpZCB3aXRoIEthbGxpc3RvIGRhdGEgY29sb3JlZCBieSBzZWxlY3Rpb24gc3RyYXRlZ3kKa2FsbGlzdG9fZ3JpZF9zdHJhdGVneSA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAga2FsbGlzdG9fcGNhX3Bsb3Rfc3RyYXRlZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMiIpICsKICAgIGdncGxvdDI6OmdndGl0bGUoIlNlbGVjdGlvbiBTdHJhdGVneSAoS2FsbGlzdG8pIiksCiAga2FsbGlzdG9fdHNuZV9wbG90X3N0cmF0ZWd5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAidC1TTkUxIiwgeSA9ICJ0LVNORTIiKSwKICBrYWxsaXN0b191bWFwX3Bsb3Rfc3RyYXRlZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJVTUFQMSIsIHkgPSAiVU1BUDIiKSwKICBsYWJlbHMgPSAiQVVUTyIsCiAgYWxpZ24gPSAidmgiLAogIGF4aXMgPSAiYiIsCiAgbmNvbCA9IDEsCiAgbnJvdyA9IDUKKQoKIyBBZGQgdGhlIGFwcHJvcHJpYXRlIGxlZ2VuZHMgdG8gdGhlIGNvd3Bsb3RzCmthbGxpc3RvX2dyaWRfaGlzdG9sb2d5IDwtCiAgY293cGxvdDo6cGxvdF9ncmlkKGthbGxpc3RvX2dyaWRfaGlzdG9sb2d5LCBsZWdlbmRfYSwgcmVsX2hlaWdodHMgPSBjKDIsIC4yKSkKCmthbGxpc3RvX2dyaWRfc3RyYXRlZ3kgPC0KICBjb3dwbG90OjpwbG90X2dyaWQoa2FsbGlzdG9fZ3JpZF9zdHJhdGVneSwgbGVnZW5kX2IsIHJlbF9oZWlnaHRzID0gYygyLCAyKSkKCiMgU2F2ZSBicm9hZCBoaXN0b2xvZ3kgcGxvdCBncmlkCmdncGxvdDI6Omdnc2F2ZSgKICBmaWxlLnBhdGgoInBsb3RzIiwgIm1ldGFfZ3JpZF9rYWxsaXN0b19oaXN0b2xvZ3kucGRmIiksCiAga2FsbGlzdG9fZ3JpZF9oaXN0b2xvZ3ksCiAgd2lkdGggPSAxMiwKICBoZWlnaHQgPSAxNQopCgojIFNhdmUgc2VsZWN0aW9uIHN0cmF0ZWd5IHBsb3QgZ3JpZApnZ3Bsb3QyOjpnZ3NhdmUoCiAgZmlsZS5wYXRoKCJwbG90cyIsICJtZXRhX2dyaWRfa2FsbGlzdG9fc3RyYXRlZ3kucGRmIiksCiAga2FsbGlzdG9fZ3JpZF9zdHJhdGVneSwKICB3aWR0aCA9IDEyLAogIGhlaWdodCA9IDE1CikKCiMgRGlzcGxheSB0aGUgcGxvdCBncmlkIGFjcm9zcyBicm9hZCBoaXN0b2xvZ2llcwprYWxsaXN0b19ncmlkX2hpc3RvbG9neQoKIyBEaXNwbGF5IHRoZSBwbG90IGdyaWQgYWNyb3NzIHNlbGVjdGlvbiBzdHJhdGVneQprYWxsaXN0b19ncmlkX3N0cmF0ZWd5CmBgYAoKIyMgUG9seS1BIEthbGxpc3RvIFBsb3RzIEdyaWQgClRoZSBncmlkIG9mIHBsb3RzIGJlbG93IHJlcHJlc2VudHMgdGhlIEthbGxpc3RvIGRhdGEgZmlsdGVyZWQgZm9yIHRoZSBgcG9seS1BYApzZWxlY3Rpb24gc3RyYXRlZ3ksIHdpdGggZGF0YSBwb2ludHMgY29sb3JlZCBieSBicm9hZCBoaXN0b2xvZ3kuIAoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTh9CiMgUnVuIHRoZSBgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uYCBmdW5jdGlvbiB0byBwbG90IGVhY2ggc2V0IG9mIGRpbWVuc2lvbgojIHJlZHVjdGlvbiBzY29yZXMgdXNpbmcgYGJyb2FkX2hpc3RvbG9neWAgdG8gY29sb3IgcG9pbnRzCnBvbHlBX2thbGxpc3RvX3BjYV9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihwb2x5QV9rYWxsaXN0b19wY2FfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDcpCnBvbHlBX2thbGxpc3RvX3RzbmVfcGxvdF9oaXN0b2xvZ3kgPC0KICBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb24ocG9seUFfa2FsbGlzdG9fdHNuZV9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgMSwgMikKcG9seUFfa2FsbGlzdG9fdW1hcF9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihwb2x5QV9rYWxsaXN0b191bWFwX2FsaWduZWRfZGYsICJicm9hZF9oaXN0b2xvZ3kiLCAxLCAyKQoKIyBQbG90IGdyaWQgd2l0aCBLYWxsaXN0byBkYXRhIGNvbG9yZWQgYnkgY2FuY2VyIGhpc3RvbG9neQpwb2x5QV9rYWxsaXN0b19ncmlkX2hpc3RvbG9neSA8LSBjb3dwbG90OjpwbG90X2dyaWQoCiAgcG9seUFfa2FsbGlzdG9fcGNhX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzciKSArCiAgICBnZ3Bsb3QyOjpnZ3RpdGxlKCJCcm9hZCBIaXN0b2xvZ3kgKEthbGxpc3RvIC0gUG9seS1BKSIpLAogIHBvbHlBX2thbGxpc3RvX3RzbmVfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJ0LVNORTEiLCB5ID0gInQtU05FMiIpLAogIHBvbHlBX2thbGxpc3RvX3VtYXBfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJVTUFQMSIsIHkgPSAiVU1BUDIiKSwKICBsYWJlbHMgPSAiQVVUTyIsCiAgYWxpZ24gPSAidmgiLAogIGF4aXMgPSAiYiIsCiAgbmNvbCA9IDEsCiAgbnJvdyA9IDcKKQoKIyBFeHRyYWN0IHRoZSBsZWdlbmQgZnJvbSBvbmUgb2YgdGhlIHBsb3RzCmxlZ2VuZF9hIDwtIGNvd3Bsb3Q6OmdldF9sZWdlbmQoCiAgcG9seUFfa2FsbGlzdG9fdW1hcF9wbG90X2hpc3RvbG9neSArCiAgICBnZ3Bsb3QyOjp0aGVtZSgKICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wMDEsIDAuNDUpLAogICAgICBsZWdlbmQua2V5LndpZHRoID0gZ2dwbG90Mjo6dW5pdCgwLjEsICJjbSIpLAogICAgICB0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkKICAgICkKKQoKIyBBZGQgdGhlIGFwcHJvcHJpYXRlIGxlZ2VuZCB0byB0aGUgY293cGxvdApwb2x5QV9rYWxsaXN0b19ncmlkX2hpc3RvbG9neSA8LQogIGNvd3Bsb3Q6OnBsb3RfZ3JpZChwb2x5QV9rYWxsaXN0b19ncmlkX2hpc3RvbG9neSwgbGVnZW5kX2EsIHJlbF9oZWlnaHRzID0gYygyLCAuMikpCgojIFNhdmUgcGxvdCBncmlkCmdncGxvdDI6Omdnc2F2ZSgKICBmaWxlLnBhdGgoInBsb3RzIiwgIm1ldGFfZ3JpZF9wb2x5QV9rYWxsaXN0b19oaXN0b2xvZ3kucGRmIiksCiAgcG9seUFfa2FsbGlzdG9fZ3JpZF9oaXN0b2xvZ3ksCiAgd2lkdGggPSAxMiwKICBoZWlnaHQgPSAxNQopCgojIERpc3BsYXkgdGhlIHBsb3QgZ3JpZCBhY3Jvc3MgYnJvYWQgaGlzdG9sb2dpZXMKcG9seUFfa2FsbGlzdG9fZ3JpZF9oaXN0b2xvZ3kKYGBgCgojIyBTdHJhbmRlZCBLYWxsaXN0byBQbG90cyBHcmlkClRoZSBncmlkIG9mIHBsb3RzIGJlbG93IHJlcHJlc2VudHMgdGhlIEthbGxpc3RvIGRhdGEgZmlsdGVyZWQgZm9yIHRoZSBgc3RyYW5kZWRgCnNlbGVjdGlvbiBzdHJhdGVneSwgd2l0aCBkYXRhIHBvaW50cyBjb2xvcmVkIGJ5IGJyb2FkIGhpc3RvbG9neS4gCgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxOH0KIyBSdW4gdGhlIGBwbG90X2RpbWVuc2lvbl9yZWR1Y3Rpb25gIGZ1bmN0aW9uIHRvIHBsb3QgZWFjaCBzZXQgb2YgZGltZW5zaW9uCiMgcmVkdWN0aW9uIHNjb3JlcyB1c2luZyBgYnJvYWRfaGlzdG9sb2d5YCB0byBjb2xvciBwb2ludHMKc3RyYW5kZWRfa2FsbGlzdG9fcGNhX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHN0cmFuZGVkX2thbGxpc3RvX3BjYV9hbGlnbmVkX2RmLCAiYnJvYWRfaGlzdG9sb2d5IiwgNCwgNykKc3RyYW5kZWRfa2FsbGlzdG9fdHNuZV9wbG90X2hpc3RvbG9neSA8LQogIHBsb3RfZGltZW5zaW9uX3JlZHVjdGlvbihzdHJhbmRlZF9rYWxsaXN0b190c25lX2FsaWduZWRfZGYsICJicm9hZF9oaXN0b2xvZ3kiLCAxLCAyKQpzdHJhbmRlZF9rYWxsaXN0b191bWFwX3Bsb3RfaGlzdG9sb2d5IDwtCiAgcGxvdF9kaW1lbnNpb25fcmVkdWN0aW9uKHN0cmFuZGVkX2thbGxpc3RvX3VtYXBfYWxpZ25lZF9kZiwgImJyb2FkX2hpc3RvbG9neSIsIDEsIDIpCgojIEV4dHJhY3QgdGhlIGxlZ2VuZCBmcm9tIG9uZSBvZiB0aGUgcGxvdHMKbGVnZW5kX2IgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZCgKICBzdHJhbmRlZF9rYWxsaXN0b191bWFwX3Bsb3RfaGlzdG9sb2d5ICsKICAgIGdncGxvdDI6OnRoZW1lKAogICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjA1LCAwLjM1KSwKICAgICAgbGVnZW5kLmtleS53aWR0aCA9IGdncGxvdDI6OnVuaXQoMC4xLCAiY20iKSwKICAgICAgdGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gOSkKICAgICkKKQoKIyBQbG90IGdyaWQgd2l0aCBLYWxsaXN0byBkYXRhIGNvbG9yZWQgYnkgYnJvYWQgaGlzdG9sb2d5CnN0cmFuZGVkX2thbGxpc3RvX2dyaWRfaGlzdG9sb2d5IDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICBzdHJhbmRlZF9rYWxsaXN0b19wY2FfcGxvdF9oaXN0b2xvZ3kgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdncGxvdDI6OmxhYnMoeCA9ICJQQzQiLCB5ID0gIlBDNyIpICsKICAgIGdncGxvdDI6OmdndGl0bGUoIkJyb2FkIEhpc3RvbG9neSAoS2FsbGlzdG8gLSBTdHJhbmRlZCkiKSwKICBzdHJhbmRlZF9rYWxsaXN0b190c25lX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAidC1TTkUxIiwgeSA9ICJ0LVNORTIiKSwKICBzdHJhbmRlZF9rYWxsaXN0b191bWFwX3Bsb3RfaGlzdG9sb2d5ICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiVU1BUDEiLCB5ID0gIlVNQVAyIiksCiAgbGFiZWxzID0gIkFVVE8iLAogIGFsaWduID0gInZoIiwKICBheGlzID0gImIiLAogIG5jb2wgPSAxLAogIG5yb3cgPSA3CikKCiMgQWRkIHRoZSBhcHByb3ByaWF0ZSBsZWdlbmQgdG8gdGhlIGNvd3Bsb3QKc3RyYW5kZWRfa2FsbGlzdG9fZ3JpZF9oaXN0b2xvZ3kgPC0KICBjb3dwbG90OjpwbG90X2dyaWQoc3RyYW5kZWRfa2FsbGlzdG9fZ3JpZF9oaXN0b2xvZ3ksIGxlZ2VuZF9iLCByZWxfaGVpZ2h0cyA9IGMoMiwgLjIpKQoKIyBTYXZlIHBsb3QgZ3JpZApnZ3Bsb3QyOjpnZ3NhdmUoCiAgZmlsZS5wYXRoKCJwbG90cyIsICJtZXRhX2dyaWRfc3RyYW5kZWRfa2FsbGlzdG9faGlzdG9sb2d5LnBkZiIpLAogIHN0cmFuZGVkX2thbGxpc3RvX2dyaWRfaGlzdG9sb2d5LAogIHdpZHRoID0gMTIsCiAgaGVpZ2h0ID0gMTUKKQoKIyBEaXNwbGF5IHRoZSBwbG90IGdyaWQgYWNyb3NzIGJyb2FkIGhpc3RvbG9naWVzCnN0cmFuZGVkX2thbGxpc3RvX2dyaWRfaGlzdG9sb2d5CmBgYAojIFNlc3Npb24gSW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==